﻿namespace Reals.Utils.Threading
{
  #region Using directives

  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Threading;

  #endregion

  /// <summary>
  /// 同步屏障.
  /// </summary>
  public interface SyncBarrier
  {
    /// <summary>
    /// 获取正在有屏障处理的线程总数.
    /// </summary>
    /// <remarks>
    /// 改变线程总数,是一个同步操作.
    /// 请在确保没有任何线程使用当前
    /// 屏障(或初始化)时改动总数.如果已经有线
    /// 程使用屏障,请使用ExchangingThreads
    /// 异步更改线程的数量.
    /// </remarks>
    int TotalThreads { get;set;}
    /// <summary>
    /// 获取或者设置等待交换的线程数.
    /// </summary>
    /// <remarks>
    /// 可以通过交换向线程总数中
    /// 添加或者删除线程.
    /// 使用这种方法主要是因为
    /// 线程同步过程中,只能在
    /// 重建屏障的时候改变线程
    /// 总数.而其他时候将造成
    /// 同步的失败.
    /// 此值为正,意味着添加
    /// 线程,相反意味着删除
    /// 线程.
    /// 当交换完成,此值自动
    /// 置零.
    /// 此外,如果线程的减少
    /// 量大于当前线程总量,
    /// 直接将总量置零,而不
    /// 考虑余量.
    /// 注意,这是一个异步操作,
    /// 屏障可能不会立即实现
    /// 这个操作.请观察此值,
    /// 以确定是否进行添加.
    /// </remarks>
    int ExchangingThreads { get;set;}
    /// <summary>
    /// 获取当前被阻碍的线程数.
    /// </summary>
    int BarricadedThreads { get;}
    /// <summary>
    /// 穿越屏障.
    /// </summary>
    /// <remarks>
    /// 线程通过调用穿越屏障,
    /// 将对控制的安排交给屏障.
    /// 当逃离条件满足,也就是
    /// 到达线程数量已经达到
    /// 要求的数量,屏障将自动
    /// 开放,而线程在此时可以
    /// 穿越.当逃离条件尚未满
    /// 足,线程将在穿越屏障的
    /// 时候被屏障阻塞.
    /// </remarks>
    void Stride();
  }

  public class SyncBarrierImpl : SyncBarrier
  {
    #region Fields
    protected int unbarricadedThreads = 0;
    protected int exchangingThreads = 0;
    protected int totalThreads = 0;
    #endregion
    #region Properties
    public virtual int BarricadedThreads
    {
      get { return this.totalThreads - this.unbarricadedThreads; }
    }
    public virtual int TotalThreads
    {
      get { return this.totalThreads; }
      set { this.Rebuild(value); }
    }
    public virtual int ExchangingThreads
    {
      get { return this.exchangingThreads; }
      set { this.exchangingThreads = value; }
    }
    #endregion
    #region Constructors
    public SyncBarrierImpl() : this(1) { }
    public SyncBarrierImpl(int totalThreads)
    {
      this.Rebuild(totalThreads);
    }
    #endregion
    #region Methods
    public virtual void Stride()
    {
      //Barrier is designed for threads, no thread takes no effect.
      //And single thread will not be barricaded.
      if (this.totalThreads > 0)
      {
        lock (this)
        {
          if (this.unbarricadedThreads > 0)
          {
            this.unbarricadedThreads--;
            //All threads arrived
            if (this.unbarricadedThreads == 0)
            {
              //Rebuild first, then pulse the signal!
              this.Rebuild(this.totalThreads + this.exchangingThreads);
              Monitor.PulseAll(this);
            }
            else//Not all threads arrived.
            {
              Monitor.Wait(this);
            }
            //Threads escapes here.
          }
        }
      }
    }

    protected virtual void Rebuild(int totalThreads)
    {
      this.unbarricadedThreads = this.totalThreads = totalThreads;
      this.exchangingThreads = 0;
    }
    #endregion
  }
}
